/*
 *    Copyright 2014 Sam Y Ge
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.strx;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Reader;

/**
 * 通过流读取Tag的TagReader
 * 
 * 读取以输入流形式的XML文档
 * 
 * @see TagReader
 * @see StringTagReader
 * 
 * @author <a href="mailto:samuel.y@foxmail.com">Sam Y Ge</a>
 */
class StreamTagReader extends TagReader {
	/** 原始字符流 */
	private Reader reader;

	/** 缓冲区 */
	private ByteArrayOutputStream buff;

	/**
	 * 通过给定的字符流来构造
	 * 
	 * @param xml
	 *            XML字符流
	 */
	StreamTagReader(Reader reader) {
		super();
		this.reader = reader;
		buff = new ByteArrayOutputStream();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	Tag getNextTag() {
		currTagStr = null;
		currTag = null;

		// 若读取过程中有错，则重置缓冲区，否则在末尾追加'>'
		byte lastByte = readUtil('>');
		if (lastByte == '>') {
			// 对读取到的文本进行预处理，当为空白时，视为无标签
			buff.write('>');
			byte[] data = buff.toByteArray();
			currTagStr = new String(data).trim();
			if (currTagStr.isEmpty()) {
				currTagStr = null;
			}
		} else {
			buff.reset();
		}

		/*
		 * 判定类型，若为开始标签，则尝试拉取后方文本； 若为CDATA标签，则忽略所有'<'和'>'直到读取到']]>'为止
		 */
		TagType type = getTagType();
		switch (type) {
		case START: {
			// 拉取StartTag的Text
			lastByte = readUtil('<');
			if (lastByte == '<') // 正常拉取
			{
				byte[] data = buff.toByteArray();
				currTagStr = new String(data).trim();
			}
			break;
		}
		case CDATA: {
			// 拉取CDATA内容
			while (true) {
				lastByte = readUtil('>');
				if (lastByte == '>') {
					// 正常拉取
					buff.write('>');
					byte[] data = buff.toByteArray();
					currTagStr = new String(data).trim();
					if (currTagStr.endsWith(Tag.CDATA_TAG_END)) {
						break;
					}
				} else {
					// 拉取出错
					currTagStr = null;
					break;
				}
			}
			break;
		}
		default: {
			// DO NOTHNIG HERE!
		}
		}

		buildTag();
		buff.reset();
		if (lastByte == '<') {
			buff.write(lastByte);
		}

		return currTag;
	}

	/**
	 * 从流读取数据，直到参数给定的终结符为止
	 * 
	 * @param stop
	 *            终结符
	 * @return 最后一个字节
	 */
	private byte readUtil(char stop) {
		byte lastByte = 0;
		while (true) {
			// 随时处理IO异常,当发生该异常时，终止解析过程
			try {
				lastByte = (byte) reader.read();

				if (lastByte == -1) {
					break;
				}

				if (lastByte == stop) {
					break;
				}

				buff.write(lastByte);
			} catch (IOException e) {
				lastByte = -1;
				break;
			}
		}

		return lastByte;
	}

	@Override
	void clean() {
		buff.reset();
	}

}
